﻿using System.Linq;
using SLNGP.Common.Extensions;
using SLNGP.Core.Fitness;
using SLNGP.Core.GeneticPrograms;
using SLNGP.Core.Individuals;
using SLNGP.Core.SecondaryStrategies;
using FitnessNumber = System.Int32;

namespace SLNGP.Core.GeneticOpearators
{
    public class TournamentCrossoverSelection : SelectionGeneticOpearator
    {
        public TournamentCrossoverSelection(int crossoverPercentage, int mutationPercentage, int carryUnchangedPercentage, int killPercentage, int tournamentSize)
            : base(crossoverPercentage, mutationPercentage, carryUnchangedPercentage, killPercentage)
        {
            this.TournamentSize = tournamentSize;
        }

        public int TournamentSize { get; set; }

        public override void Execute()
        {
            foreach (var individual in LastGeneration)
                individual.SelectionMarker = null;

            PerformTournamentCrossoverSelection();

            RandomMutationCarryoverKillSelection();
        }

        private void RandomMutationCarryoverKillSelection()
        {
            var individualsThatNeedSelection = LastGeneration.Where(i => i.SelectionMarker == null);
            foreach (var individual in individualsThatNeedSelection)
            {
                int random = Parent.GetStrategy<IRandomProvider>().Random(0, MutationPercentage + KillPercentage + CarryUnchangedPercentage);
                if (random <= MutationPercentage)
                    individual.SelectionMarker = SelectionMarkers.Mutation;
                else if (random > MutationPercentage && random < (MutationPercentage + CarryUnchangedPercentage))
                    individual.SelectionMarker = SelectionMarkers.CarryToNextGenerationUnchanged;
                else
                    individual.SelectionMarker = SelectionMarkers.Kill;
            }
        }

        private void PerformTournamentCrossoverSelection()
        {
            int numberOfIndividualsSelectedForCrossover = 0;
            int totalNumberOfIndividualsToSelect = LastGeneration.Count;
            while(numberOfIndividualsSelectedForCrossover < MathHelper.GetPercentage(totalNumberOfIndividualsToSelect, CrossoverPercentage))
            {
                IIndividual mostFitIndividualInTourtnament = null;

                int numberOfIndividualsThatParticipatedInThisTournament = 0;
                while(numberOfIndividualsThatParticipatedInThisTournament < TournamentSize)
                {
                    var individualsToChooseFrom = LastGeneration.Where(i => i.SelectionMarker == null).ToList();
                    int IndexOfIndividual = Parent.GetStrategy<IRandomProvider>().Random(0, individualsToChooseFrom.Count - 1);
                    IIndividual tournamentContender = individualsToChooseFrom[IndexOfIndividual];

                    if (mostFitIndividualInTourtnament == null)
                        mostFitIndividualInTourtnament = tournamentContender;
                    else
                    {
                        if (Parent.FitnessFunction.IsBigger(tournamentContender.Fitness, mostFitIndividualInTourtnament.Fitness))
                            mostFitIndividualInTourtnament = tournamentContender;
                    }

                    numberOfIndividualsThatParticipatedInThisTournament++;
                }

                mostFitIndividualInTourtnament.SelectionMarker = SelectionMarkers.Crossover;
                numberOfIndividualsSelectedForCrossover++;
            }
        }
    }
}